|
|
| Have You Played Atari Today? | 2600 | 5200 | 7800 | Lynx | Jaguar | Forums | Store |
Ah-ha! I've got it! Currently, the code is set up for six sprites - the player and five enemies. Each frame one sprite moves one pixel. So it takes 24 frames for a sprite to move horizontally from one PF block to the next. Now, what if I change this to fractional positioning for the enemies. The player I'll leave be because he can change direction in mid-move. What if I want to still limit myself to one full sprite movement update per frame. Okay, so let's make it take 30 frames for the enemies to move the 4 pixels. That gives the player a slight speed advantage. With fractional positioning, that's 4/30 = 0.1333 pixels per frame, or 34.1333/256 pixels per frame. Hmm... how to handle that roundoff error? Well 34*30 mod 256 = 252, so I need to somehow add 4 to the fraction over the 30 frames... Ah, heck let's just show you the code. lda xposf,x cmp #221 ; this sets the carry bit if a is >= 221 adc #34 sta xposf,x lda xpos,x adc #0 sta xpos,x Now, I have to also figure it out for the opposite direction (subtraction), but that shouldn't be too hard. I've also decided to have both the player and the enemies fall at the same rate - 8 pixels in 24 frames. I never liked that gravity was seemely different for the enemies in Lode Runner.
In Leprechaun my plans are to only go through the full movement routine for one sprite each frame. Although I haven't cycle counted the routine, I have a sneaking suspicion it will take too long to do more than one sprite per frame. (As it is, I think I'll end up having to shove it in before VSYNC so I can leave after VSYNC for setting up the sprite bitmaps. Anyway, I have two alternatives for sprite movement. I can either move only one sprite one pixel per frame (with a hack for making falling twice as fast as climbing), or I can use some kind of fractional movement and update every sprite every frame. The main disadvantage of only moving the current sprite is then the enemies will move as fast as the player. (In Lode Runner the player moves slightly faster.) I don't know whether this will be a huge disadvantage to the player or not. Factional positioning might allow different speeds, but would probably royally muck up my idea of only going through the movement routine for one sprite per frame. There's also roundoff errors unless I limit myself to inverse powers of two. I think I'll just stick with the easier first option for the moment until I get more code done.
Ugh. Leprechaun's movement routine chews up 637 bytes; over half the free space (1133 bytes) I have in BANK 3. Sigh, I'm sure I can juggle some stuff around, but fewer free bytes means fewer features.
Made some minor revisions to my tracker code and posted it to [stella]; no feedback yet (whine). One thing I'm not happy with is the byte to note ratio being 2+. This chews up ROM space much faster than I imagined. Certainly with a lot of repeated sequences, the total bytes per song will come down, but even my demo takes 168 bytes. Which kinda puts my idea of dedicating a page of ROM in Leprechaun for music to be woefully inadequate. I could probably come up with a data format which would lower the bytes/note (since I'm using one whole byte per note to store length), but it would be more costly in code space and would certainly need at least a byte more RAM. No matter how much I wish it, Leprechaun isn't writing itself. The two problems are the lack of Real Life free time and just general motivation/inertia. The problem is while I'm working on a project it's all easy. But if I stop for any significant length of time, it's really, really hard to get back to it.
logo2.zip ( 2.6k )
: 10Ta-da! This is something I've been wanting to do for a long while - a VCS music tracker format. Forgive the tacky Fuji logo, it's the first VCS program I did years ago. I re-used it so I didn't have to spend too much time putting together something which would generate a stable screen. Format details: Two layer list format: top layer is a a song which is a list of 16 bit pointers to seqences (note lists); bottom layer are sequences which are a list of 1-n byte lengths, volume, notes and escape codes. CODE Song (words): <$8000 $8000 OR word is pointer to next song entry >$7FFF word is pointer to next sequence Sequence (byte codes): byte # byte 0 $00 end of sequence 0 $nn note length in frames, followed by additional codes 1 $00 rest (AUDVn=0) for note length, volume restored 1 $0v volume control, followed by additional codes 1 $1x escape code, TDB, may be followed by additional codes 1+ %cccfffff note code note code: ccc fffff AUDC AUDF 001 0-31 1 fffff saw 010 0-7 2 fffff 010 8-31 12/13 fffff square (low) 011 0-7 3 fffff engine 011 8-31 14 fffff bass (low) 100 0-7 8 fffff noise 100 8-31 15 fffff pitfall (low) 101 0-31 4/5 fffff square (high) 110 0-31 6/10 fffff bass (high) 111 0-31 7/9 fffff pitfall (high) This covers off all the audible capabilities of the TIA. Depending on the game, songs may be either one channel (with the second channel used for SFX) or two voices. Two channel songs are stored multiplexed with the next note in the sequence being used whenever the note length counter reaches zero. De-multiplexed two channel songs could be handled, but would probably be coded similar to two independent one channel songs. SFX and two channel songs are possible, perhaps via some kind of break-in note/sequence handler. Hmmm... I'm actually impressed that "The Entertainer" came out so well. Only two sour notes. (Well, one out of tune, and one out of tone.)
Following on with my idea to re-use the player movement logic with the enemy "AI", I've been doing some thinking. Basically, the enemies will have two modes: chase & hunt (each enemy working independently). In chase mode the enemies move in the direction of the player, i.e their virtual joystick is pointed towards the player. However, if they get blocked, they flip into hunt mode. In hunt mode their virtual joystick changes direction (clockwise or counter clockwise, randomly, mirror?). They stay in hunt mode until they fall or their hunt direction matches the chase direction. I'm hoping is the enemies will avoid getting trapped in sections of the level and the hunt mode will allow them to work out "how to get there from here". Kinda a right wall/left wall follow idea. Oh, I'm also thinking that the initial hunt/chase mode & hunt direction will be part of the level data. So a level creator will be able to determine in which direction the enemies start moving. This could be away from the player!
I've been working on & off changing my if logic into 6502 ASM. Most of it is straight-forward CMP / BNE stuff. Not very complex, but a big whack of code. There is some optimization (folding & combining endpoints), but nothing major. I've pushed a couple of things down into the GRID subroutines, but I'm really starting to worry about how much code space this will require. Especially since I haven't even started thinking about the enemy logic. Which got me to thinking. How difficult would it be to adapt the player if logic to handle enemies? Well, the if logic really just converts joystick movement into actions based on the grid data. Hmm, no reason that couldn't be used for enemies. The challenge then becomes coming up with the logic to come up with "joystick" directions for the enemies. With that in mind, I'll make my movement logic code handle both enemies and players. Just have to load the right data. In fact, instead of having to always "LDX CURSPR / LDA JOY,X" etc, maybe I'll copy the handful of bytes to a temporary location so the movement logic only has to work with one location.
20050823.txt ( 3.48k )
: 15
20050824.txt ( 1.83k )
: 11A good example of top down design and bottom up programming. The spreadsheet method made designing the player movement truth table much easier than trying to figure out the if cases from scratch. From that I could then do a pseudo code version of the if statement logic (see 20050823.txt). Since it was in a spreadsheet I could do different sorts to see whether there was any benefit to doing the tests in different orders (e.g. check the button down cases before checking the different grid cases). This also gave me the chance to double check my logic (fixed a couple mistakes) and then rework some of the decisions to simplify the if statement logic. (e.g. remove some of the diagonal options.) As I did the if statements I would try to think about how I would turn it into efficient ASM. I'm a little worried about space (I really only have 2K for game logic, including the kernel), so I'm going to try to push stuff into subroutines. Note also the use of gotos. Speaking of which, 20050824.txt contains two subroutines to support the player (and enemy) movement logic. The first routine - GOTGOLD, clears one of the playfield bitmap tiles using SuperCharger writes. The second set of subroutines loads a tile from the playfield for various directions. My top down design lets me focus first on those routines which will be used most frequently. Then I program from the bottom up to make those inner functions most efficient. I can also then decide how I want to best handle the limitted number of registers.
So, I've reviewed the source code and re-aquainted myself with it. Since the kernel and the sort routines are working, my next focus is on player movement. From a code perspective this will turn into a bunch of IF statements, but first I need to come up with the truth table to ensure no conditions get missed. I'm doing this in a spreadsheet. Each row is one set of conditions. I take a generic condition (e.g. Grid = open) and break it into two conditions (Grid = open & Grid_Down = {Gold,Open,Hole,Rope} and Grid = open & Grid_Down = {Dirt,Rock,Ladder} ) then assign results to each (falling, & more conditions required). It's going to be interesting to change this into code, but it make sure I don't miss any possibilities and have thought through each "what happens if". Right now my plan is to handle player input/enemy AI & movement on a round-robin basis. So each frame only one sprite will move. The only exception to this is falling, which will be handled via a sprite index counter 180 degrees out of sync with the main counter. Now, the main disadvantage of handling it this way is both the player and the enemy will move at the same speed. The alternative is to use fractional positioning and move every sprite every frame. But that may be tough to sync with handling player input & enemy AI only every N frames. The reason for doing the player input & enemy AI only every N frames is to save clock cycles. Since I have to decode the bitmap for a lot of the decisions, that's going to take a lot of processing power.
lep40427.zip ( 15.41k )
: 17Features: - 6K SC binary w/ header (8448 bytes total) - performs both bankswitching and SC RAM writes - tested on an actual 2600+SC (back in 2004) (although I now have a 7800+CC2 as well) - functional kernel w/ sprite repositioning - semi-intelligent flicker / partial sort routine LEPRCHN.ASM : wrapper - includes, equates, ZP variables, SC header LEPBANK1.ASM : level data - background playfield bitmaps - SC RAM space for forground player/leprechaun sprite bitmaps - SC RAM space for player color / XPOS array - future music space (using Paul Slocum's tracker?) LEPBANK2.ASM : utility - everything which doesn't require level data (bank 1) access - future title / end screen and level loader LEPBANK3.ASM : kernal & game logic - as much as I can squeeze into 2K - already pushing 1K used w/ kernel.
|
vdub_bobby on Leprechaun scoring idea
EricBall on TIA Schematic - Audio Noise Generator EricBall on TIA Schematic - Audio Noise Generator EricBall on TIA Schematic - Audio Noise Generator supercat on TIA Schematic - Audio Noise Generator supercat on Apple ][ hi-res graphics EricBall on Apple ][ hi-res graphics supercat on Apple ][ hi-res graphics EricBall on Apple ][ hi-res graphics supercat on Apple ][ hi-res graphics |
| Lo-Fi Version |
©2005 AtariAge |
Time is now: Thu Feb 2, 2006 5:32 AM
Contact | Privacy Policy | Legal |